package ar.edu.unlu.sistemas.ws.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.SortedMap;
import java.util.TreeMap;

import ar.edu.unlu.sistemas.ws.test.ResultsLogProcessor.DocumentRow;
import ar.edu.unlu.sistemas.ws.test.ResultsLogProcessor.QueryResults;
import ar.edu.unlu.sistemas.ws.test.ResultsLogProcessor.SourceRow;

public class ResultsMovirdroidLemurComparator {
	
	private static final Boolean allowZeroRank = false;
	private static final String RR = "RR";
	private static final String SORT = "SORT";
	private static final String[] metodosMerge = new String[]{RR,SORT};
	private static final String[] cantidadNodos = new String[]{"1 NODO","2 NODOS","3 NODOS"};
	private static final String[] cantidadTerminos = new String[]{"1 TERM","2 TERMS","3 TERMS"};
	//private static String[] currents = new String[]{QueriesLogProcessor.UMEX_RR, QueriesLogProcessor.UMEX_SORT};
	//private static String[] currents = new String[]{QueriesLogProcessor.GRULIC_RR, QueriesLogProcessor.GRULIC_SORT};
	private static String current = QueriesLogProcessor.UMEX_SORT;
	private static QueryResults[] lemur_query_results = new QueryResults[800];
	private static Map<String,QueryResults> movir_query_results = new HashMap<String, QueryResults>();
	private static List[][][] metrics = new List[3][2][3];
	static {
		for (int i=0; i<3; i++)
			for (int j=0; j<2; j++)
				for (int k=0; k<3; k++)
					metrics[i][j][k] = new ArrayList<IRMetric>();
	}
	private static IRMetric[][][] stats = new IRMetric[3][2][3];
	/**
	 * @param args
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		
		String currentMergeMethod;
		List<QueryResults> currentMovirdroidQueryResults;
		List<DocumentRow> currentMovirdroidDocuments;
		List<DocumentRow> currentLemurDocuments;
		SortedMap<Integer,DocumentRow> movirdroidRelevantDocs;
		List<DocumentRow> lemurIntersectionDocuments;
		IRMetric metric;
		
		//LEVANTO LOS DATOS
		ObjectInputStream movir_ois = new ObjectInputStream(new FileInputStream(new File("/home/pepo/Dropbox/Tesis/Resultados/"+current.toUpperCase()+"_result_object")));
		movir_query_results = (Map<String,QueryResults>) movir_ois.readObject();
		movir_ois.close();
		ObjectInputStream lemur_ois = new ObjectInputStream(new FileInputStream(new File("/home/pepo/Dropbox/Tesis/Resultados/"+current.toUpperCase()+"_lemur_result_object")));
		lemur_query_results = (QueryResults[]) lemur_ois.readObject();
		lemur_ois.close();
		
		//ITERO POR COLECCION-TIPO_DE_MERGEO
		for (int j=0; j<2; j++) {
			currentMergeMethod = metodosMerge[j];			
			//ITERO POR N MEJORES NODOS (con 1>=N<=3)
			for (int i=0; i<3; i++) {
				//ITERO POR CANTIDAD N CANTIDAD DE TERMINOS DE LA QUERY (con 1>=N<=3)
				for (int k=0; k<3; k++) {
					currentMovirdroidQueryResults = filterQueryResults(new PredicateQueryByNTerminos(k+1));
					for (QueryResults qr : currentMovirdroidQueryResults) {
						if(lemur_query_results[qr.id]!=null) {
							currentLemurDocuments = lemur_query_results[qr.id].documents;
							currentMovirdroidDocuments = filterDocumentRows(new PredicateDocumentsByNSources(i+1, qr, allowZeroRank),qr);
							currentMovirdroidDocuments = simulateMergeMetod(currentMergeMethod,current,currentMovirdroidDocuments,qr);
							//if (currentMovirdroidDocuments.size()>10) currentMovirdroidDocuments = currentMovirdroidDocuments.subList(0,10);
							//System.out.println("\""+qr.query+"\" movirdroid:"+currentMovirdroidDocuments.size()+" lemur:"+currentLemurDocuments.size());
							metric = new IRMetric();
							metric.relevants = currentLemurDocuments.size();
							metric.retrieved = currentMovirdroidDocuments.size();
							movirdroidRelevantDocs = new TreeMap<Integer,ResultsLogProcessor.DocumentRow>();
							lemurIntersectionDocuments = new LinkedList<ResultsLogProcessor.DocumentRow>();
							for (DocumentRow dr: currentLemurDocuments) {
								if(currentMovirdroidDocuments.contains(dr)) {
									metric.intersection+=1;
									movirdroidRelevantDocs.put(currentMovirdroidDocuments.indexOf(dr),currentMovirdroidDocuments.get(currentMovirdroidDocuments.indexOf(dr)));
									lemurIntersectionDocuments.add(dr);
								}
							}
							int positionRelevant = 1;
							for (Integer index: movirdroidRelevantDocs.keySet()) {
								metric.addCorrelationData(lemurIntersectionDocuments.indexOf(movirdroidRelevantDocs.get(index))+1,positionRelevant);
								positionRelevant++;
							}
							metrics[i][j][k].add(metric);
						}
					}
				}
			}
		}
		Integer count;
		Double acPrecision;
		Double acRecall;
		Double acCorrelation;
		Integer intersection;
		IRMetric statsMetric;
		for (int i=0; i<3; i++)
			for (int j=0; j<2; j++)
				for (int k=0; k<3; k++) {
					count = 0;
					intersection = 0;
					acPrecision = 0.0;
					acRecall = 0.0;
					acCorrelation = 0.0;
					for (IRMetric irMetric : (List<IRMetric>)metrics[i][j][k]) {
						if (!irMetric.getPrecision().isNaN()){
							count++;
							intersection+=irMetric.intersection;
							acPrecision+=irMetric.getPrecision();
							acRecall+=irMetric.getRecall();
							acCorrelation+=irMetric.getCorrelation()>0?irMetric.getCorrelation():irMetric.getCorrelation()*-1;
							System.out.println(irMetric);
						}
					}
					statsMetric = new IRMetric();
					statsMetric.intersection = new Integer(Math.round(intersection/count));
					statsMetric.setPrecision(acPrecision/count);
					statsMetric.setRecall(acRecall/count);
					statsMetric.setCorrelation(acCorrelation/count);
					stats[i][j][k] = statsMetric;
				}
		//MUESTRO RESULTADOS
		FileWriter writer = new FileWriter(new File("/home/pepo/Dropbox/Tesis/Resultados/ir_stats_"+(allowZeroRank?"zeros_":"")+current+".csv"));
		writer.append("\n// PRUEBA "+current.toUpperCase()+" //\n");
		for (int i=0; i<3; i++)
			for (int j=0; j<2; j++)
				for (int k=0; k<3; k++) {
					writer.append(cantidadNodos[i]+","+cantidadTerminos[k]+","+metodosMerge[j]+","+stats[i][j][k].toString()+"\n");
				}
		writer.flush();
		writer.close();
	}
	
	public static List<DocumentRow> simulateMergeMetod(String currentMergeMethod, String currentCollection, List<DocumentRow> documents, QueryResults qr) {
		if (!currentCollection.toUpperCase().contains(currentMergeMethod)) {
			if (RR.equals(currentMergeMethod)) {
				Map<String,Queue<DocumentRow>> sourcesMap = new HashMap<String, Queue<DocumentRow>>();
				for (DocumentRow dr : documents) {
					if (!sourcesMap.containsKey(dr.source)) sourcesMap.put(dr.source, new LinkedList<ResultsLogProcessor.DocumentRow>());
					sourcesMap.get(dr.source).offer(dr);
				}
				List<DocumentRow> rrList = new LinkedList<ResultsLogProcessor.DocumentRow>();
				int vacios = 0;
				while (vacios < sourcesMap.keySet().size()) {
					for (SourceRow sr : qr.sources) {
						if (sourcesMap.get(sr.source) != null && sourcesMap.get(sr.source).size()>0) {
							rrList.add(sourcesMap.get(sr.source).poll());
							if(sourcesMap.get(sr.source).isEmpty()) vacios++;
						}
					}
				}
				return rrList;
			} else {
				Collections.sort(documents,new Comparator<DocumentRow>() {
					public int compare(DocumentRow o1, DocumentRow o2) {
						return o2.rank.compareTo(o1.rank);
					}
				});
			}
		}
		return documents;
	}
	
	private static List<QueryResults> filterQueryResults(Predicate<QueryResults> predicate) {
		List<QueryResults> filtered = new ArrayList<ResultsLogProcessor.QueryResults>();
		for (QueryResults qr : movir_query_results.values())
			if (predicate.match(qr)) filtered.add(qr);
		return filtered;
	}
	
	private static List<DocumentRow> filterDocumentRows(Predicate<DocumentRow> predicate, QueryResults qr) {
		List<DocumentRow> filtered = new ArrayList<ResultsLogProcessor.DocumentRow>();
		for (DocumentRow dr : qr.documents)
			if (predicate.match(dr)) filtered.add(dr);
		return filtered;
	}
	
	private static class IRMetric {
		Integer relevants;
		Integer retrieved;
		Integer intersection = 0;
		private Double correlation;
		private Double precision;
		private Double recall;
		private Double d2 = 0.0;
		Double getPrecision() {
			if (this.precision == null ) {
				this.precision = new Double(this.intersection) / this.retrieved;
			}
			return this.precision;
		}
		Double getRecall() {
			if (this.recall == null ) {
				this.recall = new Double(this.intersection) / this.relevants;
			}
			return this.recall;
		}
		void setCorrelation(Double correlation) {
			this.correlation = correlation;
		}
		void setPrecision(Double precision) {
			this.precision = precision;
		}
		void setRecall(Double recall) {
			this.recall = recall;
		}
		void addCorrelationData(Integer x, Integer y) {
			this.d2 += Math.pow(x-y, 2);
		}
		Double getCorrelation() {
			if (this.correlation == null) {
				if (this.intersection > 1) this.correlation = 1-((6*this.d2)/(this.intersection*(Math.pow(this.intersection, 2)-1)));
				else if (this.intersection == 1) this.correlation = this.getPrecision();
				else this.correlation = 0.0;
			}
			return this.correlation;
		}
		@Override
		public String toString() {
			return this.intersection+","+this.getPrecision()+","+this.getRecall()+","+this.getCorrelation();
		}
		
	}
	
	private static interface Predicate<T> {
		abstract boolean match(T t);
	}
	
	private static class PredicateQueryByNTerminos implements Predicate<QueryResults> {
		private Integer nTerminos;
		PredicateQueryByNTerminos (Integer n) {
			this.nTerminos = n;
		}
		public boolean match(QueryResults t) {
			return t.query.split("\\s").length == nTerminos.intValue();
		}
		
	}
	
	private static class PredicateDocumentsByNSources implements Predicate<DocumentRow> {
		private List<String> sources;
		private Boolean allowZeroRank;
		PredicateDocumentsByNSources (Integer n, QueryResults qr, Boolean allowZeroRank) {
			this.sources = new ArrayList<String>();
			this.allowZeroRank = allowZeroRank;
			for (SourceRow sr : qr.sources.subList(0, n))
				this.sources.add(sr.source);
		}
		public boolean match(DocumentRow t) {
			return (this.allowZeroRank || (!this.allowZeroRank && t.rank >0)) && this.sources.contains(t.source);
		}
		
	}

}
